import crypto from 'crypto'
import axios from 'axios'
import fs from 'fs'
import path from 'path'
import {fileURLToPath} from 'url'
import {wechatPayConfig} from '../config/wechat.js'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

/**
 * 微信支付 API V3 工具类
 */
class WechatPayV3 {
  constructor() {
    this.appId = wechatPayConfig.appId
    this.mchId = wechatPayConfig.mchId
    this.apiV3Key = process.env.WECHAT_API_V3_KEY
    this.serialNo = process.env.WECHAT_SERIAL_NO
    this.privateKey = this.loadPrivateKey()
    
    // 平台证书缓存
    this.platformCertificates = new Map()
    this.certificatesLastUpdate = null
    this.certificatesUpdateInterval = 12 * 60 * 60 * 1000 // 12小时更新一次
    
    // 证书存储路径
    this.certCacheDir = path.join(__dirname, '../../certs/platform')
    this.ensureCertCacheDir()
    
    // 加载配置的平台证书
    this.loadConfiguredPlatformCert()
  }

  /**
   * 确保证书缓存目录存在
   */
  ensureCertCacheDir() {
    try {
      if (!fs.existsSync(this.certCacheDir)) {
        fs.mkdirSync(this.certCacheDir, {recursive: true})
      }
    } catch (error) {
      console.error('创建证书缓存目录失败:', error)
    }
  }

  /**
   * 加载商户私钥
   */
  loadPrivateKey() {
    const keyPath = process.env.WECHAT_PRIVATE_KEY_PATH
    if (!keyPath) {
      console.warn('未配置商户私钥路径 WECHAT_PRIVATE_KEY_PATH')
      return null
    }

    try {
      return fs.readFileSync(keyPath, 'utf8')
    } catch (error) {
      console.error('加载商户私钥失败:', error)
      return null
    }
  }

  /**
   * 加载配置的平台证书
   */
  loadConfiguredPlatformCert() {
    const certPath = process.env.WECHAT_PLATFORM_CERT_PATH
    if (!certPath) {
      console.log('未配置平台证书路径，将在需要时自动下载')
      return
    }

    try {
      const certificate = fs.readFileSync(certPath, 'utf8');
      
      // 从证书中提取序列号
      const serialNo = process.env.WECHAT_PLATFORM_SERIAL_NO
      
      if (serialNo) {
        this.platformCertificates.set(serialNo, certificate)
        console.log(`已加载配置的平台证书: ${serialNo}`)
        this.certificatesLastUpdate = Date.now()
      } else {
        console.warn('无法从证书中提取序列号')
      }
    } catch (error) {
      console.error('加载平台证书失败:', error.message)
    }
  }

  /**
   * 从证书中提取序列号
   * @param {string} certificate - PEM 格式的证书
   * @returns {string|null} 证书序列号（大写十六进制）
   */
  extractSerialNoFromCert(certificate) {
    try {
      // 使用 Node.js 的 crypto 模块解析证书
      const cert = new crypto.X509Certificate(certificate)
      // 获取序列号并转换为大写十六进制
      return cert.serialNumber.toUpperCase()
    } catch (error) {
      console.error('提取证书序列号失败:', error.message)
      return null
    }
  }

  /**
   * 生成随机字符串
   */
  generateNonceStr(length = 32) {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    let result = ''
    for (let i = 0; i < length; i++) {
      result += chars.charAt(Math.floor(Math.random() * chars.length))
    }
    return result
  }

  /**
   * 生成时间戳（秒）
   */
  generateTimestamp() {
    return Math.floor(Date.now() / 1000).toString()
  }

  /**
   * 构建签名串
   * @param {string} method - HTTP 方法
   * @param {string} url - 请求 URL（不含域名）
   * @param {string} timestamp - 时间戳
   * @param {string} nonceStr - 随机字符串
   * @param {string} body - 请求体
   */
  buildSignMessage(method, url, timestamp, nonceStr, body) {
    return `${method}\n${url}\n${timestamp}\n${nonceStr}\n${body}\n`
  }

  /**
   * 生成签名
   * @param {string} message - 待签名字符串
   */
  sign(message) {
    if (!this.privateKey) {
      throw new Error('商户私钥未配置')
    }

    const sign = crypto.createSign('RSA-SHA256')
    sign.update(message)
    return sign.sign(this.privateKey, 'base64')
  }

  /**
   * 构建 Authorization 头
   * @param {string} method - HTTP 方法
   * @param {string} url - 请求 URL（不含域名）
   * @param {string} body - 请求体
   */
  buildAuthorizationHeader(method, url, body = '') {
    const timestamp = this.generateTimestamp()
    const nonceStr = this.generateNonceStr()
    const message = this.buildSignMessage(method, url, timestamp, nonceStr, body)
    const signature = this.sign(message)

    return `WECHATPAY2-SHA256-RSA2048 mchid="${this.mchId}",nonce_str="${nonceStr}",signature="${signature}",timestamp="${timestamp}",serial_no="${this.serialNo}"`
  }

  /**
   * JSAPI 下单（小程序支付）
   * @param {Object} params - 订单参数
   */
  async createOrder(params) {
    const url = '/v3/pay/transactions/jsapi'
    const body = JSON.stringify({
      appid: this.appId,
      mchid: this.mchId,
      description: params.description,
      out_trade_no: params.out_trade_no,
      notify_url: wechatPayConfig.notifyUrl,
      amount: {
        total: params.total_fee,
        currency: 'CNY'
      },
      payer: {
        openid: params.openid
      }
    })

    try {
      const authorization = this.buildAuthorizationHeader('POST', url, body)

      const response = await axios.post(`https://api.mch.weixin.qq.com${url}`, body, {
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'Authorization': authorization
        }
      })

      return {
        success: true,
        data: response.data
      }
    } catch (error) {
      console.error('JSAPI 下单失败:', error.response?.data || error.message)
      return {
        success: false,
        error: error.response?.data?.message || error.message
      }
    }
  }

  /**
   * 生成小程序调起支付参数
   * @param {string} prepayId - 预支付交易会话标识
   */
  generatePayParams(prepayId) {
    const timestamp = this.generateTimestamp()
    const nonceStr = this.generateNonceStr()
    const packageStr = `prepay_id=${prepayId}`

    // 构建签名串
    const message = `${this.appId}\n${timestamp}\n${nonceStr}\n${packageStr}\n`
    const paySign = this.sign(message)

    return {
      timeStamp: timestamp,
      nonceStr: nonceStr,
      package: packageStr,
      signType: 'RSA',
      paySign: paySign
    }
  }

  /**
   * 查询订单
   * @param {string} outTradeNo - 商户订单号
   */
  async queryOrder(outTradeNo) {
    const url = `/v3/pay/transactions/out-trade-no/${outTradeNo}?mchid=${this.mchId}`

    try {
      const authorization = this.buildAuthorizationHeader('GET', url)

      const response = await axios.get(`https://api.mch.weixin.qq.com${url}`, {
        headers: {
          'Accept': 'application/json',
          'Authorization': authorization
        }
      })

      return {
        success: true,
        data: response.data
      }
    } catch (error) {
      console.error('查询订单失败:', error.response?.data || error.message)
      return {
        success: false,
        error: error.response?.data?.message || error.message
      }
    }
  }

  /**
   * 关闭订单
   * @param {string} outTradeNo - 商户订单号
   */
  async closeOrder(outTradeNo) {
    const url = `/v3/pay/transactions/out-trade-no/${outTradeNo}/close`
    const body = JSON.stringify({
      mchid: this.mchId
    })

    try {
      const authorization = this.buildAuthorizationHeader('POST', url, body)

      await axios.post(`https://api.mch.weixin.qq.com${url}`, body, {
        headers: {
          'Content-Type': 'application/json',
          'Authorization': authorization
        }
      })

      return {
        success: true
      }
    } catch (error) {
      console.error('关闭订单失败:', error.response?.data || error.message)
      return {
        success: false,
        error: error.response?.data?.message || error.message
      }
    }
  }

  /**
   * 申请退款
   * @param {Object} params - 退款参数
   */
  async refund(params) {
    const url = '/v3/refund/domestic/refunds'
    const body = JSON.stringify({
      out_trade_no: params.out_trade_no,
      out_refund_no: params.out_refund_no,
      reason: params.reason || '用户申请退款',
      amount: {
        refund: params.refund_fee,
        total: params.total_fee,
        currency: 'CNY'
      }
    })

    try {
      const authorization = this.buildAuthorizationHeader('POST', url, body)

      const response = await axios.post(`https://api.mch.weixin.qq.com${url}`, body, {
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'Authorization': authorization
        }
      })

      return {
        success: true,
        data: response.data
      }
    } catch (error) {
      console.error('申请退款失败:', error.response?.data || error.message)
      return {
        success: false,
        error: error.response?.data?.message || error.message
      }
    }
  }

  /**
   * 查询退款
   * @param {string} outRefundNo - 商户退款单号
   */
  async queryRefund(outRefundNo) {
    const url = `/v3/refund/domestic/refunds/${outRefundNo}`

    try {
      const authorization = this.buildAuthorizationHeader('GET', url)

      const response = await axios.get(`https://api.mch.weixin.qq.com${url}`, {
        headers: {
          'Accept': 'application/json',
          'Authorization': authorization
        }
      })

      return {
        success: true,
        data: response.data
      }
    } catch (error) {
      console.error('查询退款失败:', error.response?.data || error.message)
      return {
        success: false,
        error: error.response?.data?.message || error.message
      }
    }
  }

  /**
   * 下载平台证书
   */
  async downloadCertificates() {
    const url = '/v3/certificates'

    try {
      const authorization = this.buildAuthorizationHeader('GET', url)

      const response = await axios.get(`https://api.mch.weixin.qq.com${url}`, {
        headers: {
          'Accept': 'application/json',
          'Authorization': authorization
        }
      })

      const certificates = response.data.data

      if (!certificates || certificates.length === 0) {
        throw new Error('未获取到平台证书')
      }

      // 解密并缓存证书
      for (const cert of certificates) {
        const {serial_no, encrypt_certificate} = cert

        // 解密证书
        const decryptedCert = this.decryptCertificate(encrypt_certificate)

        if (decryptedCert) {
          // 缓存到内存
          this.platformCertificates.set(serial_no, decryptedCert)

          // 保存到文件
          this.saveCertificateToFile(serial_no, decryptedCert)

          console.log(`平台证书已缓存: ${serial_no}`)
        }
      }

      this.certificatesLastUpdate = Date.now()

      return {
        success: true,
        count: certificates.length
      }
    } catch (error) {
      console.error('下载平台证书失败:', error.response?.data || error.message)
      return {
        success: false,
        error: error.response?.data?.message || error.message
      }
    }
  }

  /**
   * 解密证书
   * @param {Object} encryptCertificate - 加密的证书信息
   */
  decryptCertificate(encryptCertificate) {
    const {ciphertext, associated_data, nonce} = encryptCertificate

    try {
      // 将 base64 密文转换为 Buffer
      const ciphertextBuffer = Buffer.from(ciphertext, 'base64')

      // AEAD_AES_256_GCM 算法中，AuthTag 是密文的最后 16 字节
      const authTagLength = 16
      const authTag = ciphertextBuffer.slice(-authTagLength)
      const encryptedData = ciphertextBuffer.slice(0, -authTagLength)

      // 使用 AEAD_AES_256_GCM 解密
      const decipher = crypto.createDecipheriv(
        'aes-256-gcm',
        this.apiV3Key,
        nonce
      )

      // 设置附加数据
      decipher.setAAD(Buffer.from(associated_data, 'utf8'))

      // 设置 AuthTag
      decipher.setAuthTag(authTag)

      // 解密
      let decrypted = decipher.update(encryptedData, null, 'utf8')
      decrypted += decipher.final('utf8')

      return decrypted
    } catch (error) {
      console.error('解密证书失败:', error.message)
      console.error('请检查 API V3 密钥是否正确（应为32字节）')
      return null
    }
  }

  /**
   * 保存证书到文件
   * @param {string} serialNo - 证书序列号
   * @param {string} certificate - 证书内容
   */
  saveCertificateToFile(serialNo, certificate) {
    try {
      const filePath = path.join(this.certCacheDir, `${serialNo}.pem`)
      fs.writeFileSync(filePath, certificate, 'utf8')
    } catch (error) {
      console.error('保存证书到文件失败:', error)
    }
  }

  /**
   * 从文件加载证书
   * @param {string} serialNo - 证书序列号
   */
  loadCertificateFromFile(serialNo) {
    try {
      const filePath = path.join(this.certCacheDir, `${serialNo}.pem`)
      if (fs.existsSync(filePath)) {
        return fs.readFileSync(filePath, 'utf8')
      }
    } catch (error) {
      console.error('从文件加载证书失败:', error)
    }
    return null
  }

  /**
   * 获取平台证书
   * @param {string} serialNo - 证书序列号
   */
  async getPlatformCertificate(serialNo) {
    // 1. 从内存缓存中查找
    if (this.platformCertificates.has(serialNo)) {
      return this.platformCertificates.get(serialNo)
    }

    // 2. 从文件缓存中加载
    const certFromFile = this.loadCertificateFromFile(serialNo)
    if (certFromFile) {
      this.platformCertificates.set(serialNo, certFromFile)
      return certFromFile
    }

    // 3. 检查是否需要更新证书
    const now = Date.now()
    if (
      !this.certificatesLastUpdate ||
      now - this.certificatesLastUpdate > this.certificatesUpdateInterval
    ) {
      console.log('证书缓存已过期，重新下载...')
      await this.downloadCertificates()

      // 再次尝试从缓存中获取
      if (this.platformCertificates.has(serialNo)) {
        return this.platformCertificates.get(serialNo)
      }
    }

    return null
  }

  /**
   * 验证回调签名
   * @param {Object} headers - 请求头
   * @param {string} body - 请求体
   */
  async verifySignature(headers, body) {
    const timestamp = headers['wechatpay-timestamp']
    const nonce = headers['wechatpay-nonce']
    const signature = headers['wechatpay-signature']
    const serialNo = headers['wechatpay-serial']

    if (!timestamp || !nonce || !signature || !serialNo) {
      console.error('回调签名验证失败: 缺少必要的请求头')
      return false
    }

    // 验证时间戳（防止重放攻击）
    const now = Math.floor(Date.now() / 1000)
    const requestTime = parseInt(timestamp, 10)
    if (Math.abs(now - requestTime) > 300) {
      // 超过5分钟
      console.error('回调签名验证失败: 时间戳超出有效范围')
      return false
    }

    // 构建验签串
    const message = `${timestamp}\n${nonce}\n${body}\n`

    try {
      // 获取平台证书
      const certificate = await this.getPlatformCertificate(serialNo)

      if (!certificate) {
        console.error('回调签名验证失败: 未找到平台证书', serialNo)
        return false
      }

      // 使用平台证书公钥验签
      const verify = crypto.createVerify('RSA-SHA256')
      verify.update(message)
      const isValid = verify.verify(certificate, signature, 'base64')

      if (!isValid) {
        console.error('回调签名验证失败: 签名不匹配')
      }

      return isValid
    } catch (error) {
      console.error('回调签名验证失败:', error)
      return false
    }
  }

  /**
   * 解密回调数据
   * @param {Object} resource - 加密数据
   */
  decryptData(resource) {
    const {ciphertext, associated_data, nonce} = resource

    try {
      console.log('=== 解密回调数据调试信息 ===')
      console.log('密文长度:', ciphertext ? ciphertext.length : 0)
      console.log('AAD:', associated_data)
      console.log('Nonce 长度:', nonce ? nonce.length : 0)
      console.log('API V3 密钥长度:', this.apiV3Key ? Buffer.from(this.apiV3Key).length : 0)

      // 验证必要参数
      if (!ciphertext || !associated_data || !nonce) {
        throw new Error('缺少必要的解密参数')
      }

      if (!this.apiV3Key) {
        throw new Error('未配置 API V3 密钥')
      }

      // 验证 API V3 密钥长度
      const keyLength = Buffer.from(this.apiV3Key).length
      if (keyLength !== 32) {
        throw new Error(`API V3 密钥长度错误: ${keyLength} 字节（应为 32 字节）`)
      }

      // 将 base64 密文转换为 Buffer
      const ciphertextBuffer = Buffer.from(ciphertext, 'base64')
      console.log('密文 Buffer 长度:', ciphertextBuffer.length)

      // AEAD_AES_256_GCM 算法中，AuthTag 是密文的最后 16 字节
      const authTagLength = 16
      if (ciphertextBuffer.length < authTagLength) {
        throw new Error(`密文太短: ${ciphertextBuffer.length} 字节（至少需要 ${authTagLength} 字节）`)
      }

      const authTag = ciphertextBuffer.slice(-authTagLength)
      const encryptedData = ciphertextBuffer.slice(0, -authTagLength)

      console.log('AuthTag 长度:', authTag.length)
      console.log('加密数据长度:', encryptedData.length)

      // 使用 AEAD_AES_256_GCM 解密
      const decipher = crypto.createDecipheriv(
        'aes-256-gcm',
        this.apiV3Key,
        nonce
      )

      // 设置附加数据
      decipher.setAAD(Buffer.from(associated_data, 'utf8'))

      // 设置 AuthTag
      decipher.setAuthTag(authTag)

      // 解密
      let decrypted = decipher.update(encryptedData, null, 'utf8')
      decrypted += decipher.final('utf8')

      console.log('解密成功 ✅')
      console.log('明文长度:', decrypted.length)

      return JSON.parse(decrypted)
    } catch (error) {
      console.error('解密回调数据失败 ❌')
      console.error('错误类型:', error.name)
      console.error('错误信息:', error.message)
      console.error('错误堆栈:', error.stack)
      
      // 提供更详细的错误提示
      if (error.message.includes('unable to authenticate data')) {
        console.error('提示: 可能的原因:')
        console.error('  1. API V3 密钥不正确')
        console.error('  2. 密文数据被篡改')
        console.error('  3. AAD 不匹配')
        console.error('  4. Nonce 不正确')
      }
      
      return null
    }
  }
}

export default new WechatPayV3()
